﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Properties;

namespace Borg
{
    public class SlotDefinition : RepositoryMetaElement
    {
        internal static int nextId = 1;

        internal static QualificationState unqualifiedState =
            QualificationState.QualificationMissing |
            QualificationState.SlotDefinitionAlreadyExists;

        internal static QualificationState warningState =
            QualificationState.None;

        internal static SlotDefinition emptySlotDefinition =
            new SlotDefinition(null, new SlotDefinitionAttribute(string.Empty), string.Empty);

        internal readonly SlotDefinitionAttribute slotDefinitionAttr;
        internal ContractInfo contractInfo;
        internal RepositoryCollection<ParamDefinition> paramDefinitions;

        internal SlotDefinition derivedFrom;
        internal Type type;
        internal string typeName;

        public override string Name
        {
            get { return slotDefinitionAttr.Name; }
        }

        public override Assembly Assembly
        {
            get { return contractInfo.assembly; }
        }

        internal string FullTypeName
        {
            get { return type.FullName; }
        }

        public Type Type
        {
            get { return type; }
        }

        public string TypeName
        {
            get { return typeName; }
        }

        public ContractInfo ContractInfo
        {
            get { return contractInfo; }
        }

        public override bool IsQualified
        {
            get { return (qualificationState & unqualifiedState) == QualificationState.None; }
        }

        public SlotDefinition DerivedFrom
        {
            get { return derivedFrom; }
        }

        public RepositoryCollection<ParamDefinition> ParamDefinitions
        {
            get { return paramDefinitions; }
        }

        public static SlotDefinition Empty
        {
            get { return emptySlotDefinition; }
        }

        static SlotDefinition()
        {
            emptySlotDefinition.paramDefinitions =
                new RepositoryCollection<ParamDefinition>(new List<ParamDefinition>());
        }

        internal SlotDefinition(ContractInfo contract, SlotDefinitionAttribute slotDefinitionAttr, string typeName)
            : this(contract, slotDefinitionAttr)
        {
            this.typeName = typeName;
        }

        internal SlotDefinition(ContractInfo contract, SlotDefinitionAttribute slotDefinitionAttr, Type type)
            : this(contract, slotDefinitionAttr)
        {
            this.type = type;
            typeName = type.FullName;
        }

        private SlotDefinition(ContractInfo contract, SlotDefinitionAttribute slotDefinitionAttr)
        {
            id = nextId++;
            this.slotDefinitionAttr = slotDefinitionAttr;
            contractInfo = contract;
        }

        internal override QualificationState InternalQualify()
        {
            var errMessage = string.Empty;

            var state = Runtime.Repository.SlotDefinitions.
                Any(slotDefinition => slotDefinition != this &&
                    slotDefinition.Name == Name && slotDefinition.IsQualified);

            if (state)
            {
                errMessage = string.Format("\n  {0}", Messages.SlotDefinitionAlreadyExists);
                var slotDefinition = this;
                var status = (long)(slotDefinition.qualificationState | QualificationState.SlotDefinitionAlreadyExists);
                slotDefinition.qualificationState = (QualificationState)status;
            }
            else
            {
                var slotDefinition = this;
                var status = (long)(slotDefinition.qualificationState & ~QualificationState.SlotDefinitionAlreadyExists);
                slotDefinition.qualificationState = (QualificationState)status;
            }

            var slotDef = this;
            var stat = (long)(slotDef.qualificationState & ~QualificationState.QualificationMissing);
            slotDef.qualificationState = (QualificationState)stat;

            RuntimeBase.LogQualifyResult(
                this,
                qualificationState,
                unqualifiedState,
                warningState,
                Messages.QualifySlotDefinition,
                errMessage);

            foreach (var repositoryMetaElement in paramDefinitions)
                repositoryMetaElement.ForceQualify();

            return qualificationState;
        }
    }
}

